﻿using System;
using Reflector.CodeModel;

namespace CallGraphExtractor.CecilAdapter
{
    class Instruction : IInstruction
    {
        #region Fields

        private Mono.Cecil.Cil.Instruction mInstruction;

        #endregion Fields

        #region Lifecycle

        public Instruction(Mono.Cecil.Cil.Instruction instruction)
        {
            mInstruction = instruction;
        }

        #endregion Lifecycle

        #region Methods

        private object Adapt(object instructionOperand)
        {
            var mr = instructionOperand as Mono.Cecil.MethodReference;
            if (mr != null)
            {
                var md = mr.Resolve();
                if (md == null)
                    throw new NotSupportedException("Resolve() not supported for " + mr.FullName + ".");
                return new MethodDeclaration(md);
            }

            var fr = instructionOperand as Mono.Cecil.FieldReference;
            if (fr != null)
            {
                var fd = fr.Resolve();
                if (fd == null)
                    throw new NotSupportedException("Resolve() not supported for " + fr.FullName + " as instruction operand.");
                return new FieldDeclaration(fd);
            }

            var pr = instructionOperand as Mono.Cecil.PropertyReference;
            if (pr != null)
            {
                var pd = pr.Resolve();
                if (pd == null)
                    throw new NotSupportedException("Resolve() not supported for " + pr.FullName + ".");
                return new PropertyDeclaration(pd);
            }

            var er = instructionOperand as Mono.Cecil.EventReference;
            if (er != null)
            {
                var ed = er.Resolve();
                if (ed == null)
                    throw new NotSupportedException("Resolve() not supported for " + er.FullName + ".");
                return new EventDeclaration(ed);
            }
#if EXTR_DEBUG
            if (instructionOperand != null)
                Debug.WriteLine(instructionOperand.ToString() + " instruction operand not adapted.");
#endif
            return null;
        }

        #endregion Methods

        #region IInstruction Members

        public int Code
        {
            get { return (int)mInstruction.OpCode.Code; }
            set { throw new NotSupportedException(); }
        }

        public int Offset
        {
            get
            {
                throw new NotImplementedException();
            }
            set
            {
                throw new NotImplementedException();
            }
        }

        public object Value
        {
            get { return Adapt(mInstruction.Operand); }//TODO cast and box
            set { throw new NotSupportedException(); }
        }

        #endregion
    }
}
